#ifndef WALKERS_H_
#define WALKERS_H_

#include "../model/Value.h"

template<typename type>
class PropWalker {
public:
	virtual void handle(Prop<type> *p)=0;
};

template<typename type>
class NPropWalker {
public:
	virtual void handle(NProp<type> *p)=0;
};

class ModelWalker {
public:
	ModelWalker() {
		intW=0;
		floatW=0;
		doubleW=0;
		longW=0;
		stringW=0;
		modelW=0;
		nintW=0;
		nfloatW=0;
		ndoubleW=0;
		nlongW=0;
		nstringW=0;
		nmodelW=0;
	}
	virtual void handle(Model *m)=0;
protected:
	void handleProp(AbstractProp *p) {
		if (p->isN()) {
			switch (p->getType()) {
			case INT:
				if (nintW==0) return;
				nintW->handle((NProp<int>*)p);
				break:
			case FLOAT:
				if (nfloatW==0) return;
				nfloatW->handle((NProp<float>*)p);
				break:
			case LONG:
				if (nlongW==0) return;
				nlongW->handle((NProp<long>*)p);
				break:
			case DOUBLE:
				if (ndoubleW==0) return;
				ndoubleW->handle((NProp<double>*)p);
				break:
			case TEXT:
				if (nstringW==0) return;
				nstringW->handle((NProp<std::string>*)p);
				break:
			case MODEL:
				if (nmodelW==0) return;
				nmodelW->handle((NProp<Model*>*)p);
				break:

			}
		}
		else {
			switch (p->getType()) {
			case INT:
				if (intW==0) return;
				intW->handle((Prop<int>*)p);
				break:
			case FLOAT:
				if (floatW==0) return;
				floatW->handle((Prop<float>*)p);
				break:
			case LONG:
				if (longW==0) return;
				longW->handle((Prop<long>*)p);
				break:
			case DOUBLE:
				if (doubleW==0) return;
				doubleW->handle((Prop<double>*)p);
				break:
			case TEXT:
				if (stringW==0) return;
				stringW->handle((Prop<std::string>*)p);
				break:
			case MODEL:
				if (modelW==0) return;
				modelW->handle((Prop<Model*>*)p);
				break:

			}
		}
	}
	PropWalker<int> *intW;
	PropWalker<float> *floatW;
	PropWalker<double> *doubleW;
	PropWalker<long> *longW;
	PropWalker<std::string> *stringW;
	PropWalker<Model*> *modelW;
	NPropWalker<int> *nintW;
	NPropWalker<float> *nfloatW;
	NPropWalker<double> *ndoubleW;
	NPropWalker<long> *nlongW;
	NPropWalker<std::string> *nstringW;
	NPropWalker<Model*> *nmodelW;
};

class RecursePropWalker: public PropWalker<Model*> {
public:
	RecursePropWalker(ModelWalker *_walker) {
		walker = _walker;
	}
	void handle(Prop<Model*> *p) {
		walker->handle(p->get());
	}
private:
	ModelWalker *walker;
};

class RecurseNPropWalker: public NPropWalker<Model*> {
public:
	RecurseNPropWalker(ModelWalker *_walker) {
		walker = _walker;
	}
	void handle(NProp<Model*> *p) {
		int i;
		for (i=0;i<p->size();i++)
			walker->handle(p->get(i));
	}
private:
	ModelWalker *walker;
};

class DFModelWalker : public ModelWalker {
public:
	DFModelWalker() :ModelWalker() {
		nmodelW = new RecurseNPropWalker(this);
		modelW = new RecursePropWalker(this);
	}
	void handle(Model *m) {
		std::list<AbstractProp*> *list = m->getProps();
		std::list<AbstractProp*>::iterator it=list->begin();
		for (;it != list->end();it++) {
			AbstractProp *a = (AbstractProp*)*it;
			if (a->getType()==MODEL) {
				handleProp(a);
			}
		}
		for (;it != list->end();it++) {
			AbstractProp *a = (AbstractProp*)*it;
			if (a->getType()!=MODEL) {
				handleProp(a);
			}
		}
	}
};

class BFModelWalker : public ModelWalker {
public:
	BFModelWalker() :ModelWalker() {
		nmodelW = new RecurseNPropWalker(this);
		modelW = new RecursePropWalker(this);
	}
	void handle(Model *m) {
		std::list<AbstractProp*> *list = m->getProps();
		std::list<AbstractProp*>::iterator it=list->begin();
		for (;it != list->end();it++) {
			AbstractProp *a = (AbstractProp*)*it;
			if (a->getType()!=MODEL) {
				handleProp(a);
			}
		}
		for (;it != list->end();it++) {
			AbstractProp *a = (AbstractProp*)*it;
			if (a->getType()==MODEL) {
				handleProp(a);
			}
		}
	}
};


#endif /*WALKERS_H_*/
